<?php

/**
 * JPGraph v4.0.3
 */

namespace lemon\JpGraph\Plot;

use lemon\JpGraph\Text;
use lemon\JpGraph\Util;

/**
 * File:		JPGRAPH_PIE3D.PHP
 * // Description: 3D Pie plot extension for JpGraph
 * // Created:	 2001-03-24
 * // Ver:		 $Id: jpgraph_pie3d.php 1329 2009-06-20 19:23:30Z ljp $
 * //
 * // Copyright (c) Asial Corporation. All rights reserved.
 */

/**
 * @class PiePlot3D
 * // Description: Plots a 3D pie with a specified projection
 * // angle between 20 and 70 degrees.
 */
class PiePlot3D extends PiePlot
{
	private $labelhintcolor = 'red';
	private $showlabelhint  = true;
	private $angle		  = 50;
	private $edgecolor	  = '';
	private $edgeweight	 = 1;
	private $iThickness	 = false;

	/**
	 * CONSTRUCTOR.
	 *
	 * @param mixed $data
	 */
	public function __construct($data)
	{
		$this->radius = 0.5;
		$this->data   = $data;
		$this->title  = new Text\Text('');
		$this->title->SetFont(FF_FONT1, FS_BOLD);
		$this->value = new DisplayValue();
		$this->value->Show();
		$this->value->SetFormat('%.0f%%');
	}

	/**
	 * PUBLIC METHODS.
	 *
	 * @param mixed $aLegend
	 */
	// Set label arrays
	public function SetLegends($aLegend)
	{
		$this->legends = array_reverse(array_slice($aLegend, 0, safe_count($this->data)));
	}

	public function SetSliceColors($aColors)
	{
		$this->setslicecolors = $aColors;
	}

	public function Legend($aGraph)
	{
		parent::Legend($aGraph);
		$aGraph->legend->txtcol = array_reverse($aGraph->legend->txtcol);
	}

	public function SetCSIMTargets($aTargets, $aAlts = '', $aWinTargets = '')
	{
		$this->csimtargets	= $aTargets;
		$this->csimwintargets = $aWinTargets;
		$this->csimalts	   = $aAlts;
	}

	// Should the slices be separated by a line? If color is specified as "" no line
	// will be used to separate pie slices.
	public function SetEdge($aColor = 'black', $aWeight = 1)
	{
		$this->edgecolor  = $aColor;
		$this->edgeweight = $aWeight;
	}

	// Specify projection angle for 3D in degrees
	// Must be between 20 and 70 degrees
	public function SetAngle($a)
	{
		if ($a < 5 || $a > 90) {
			Util\JpGraphError::RaiseL(14002);
		//("PiePlot3D::SetAngle() 3D Pie projection angle must be between 5 and 85 degrees.");
		} else {
			$this->angle = $a;
		}
	}

	public function Add3DSliceToCSIM($i, $xc, $yc, $height, $width, $thick, $sa, $ea)
	{
		//Slice number, ellipse centre (x,y), height, width, start angle, end angle

		$sa *= M_PI / 180;
		$ea *= M_PI / 180;

		//add coordinates of the centre to the map
		$coords = "${xc}, ${yc}";

		//add coordinates of the first point on the arc to the map
		$xp = floor($width * cos($sa) / 2 + $xc);
		$yp = floor($yc - $height * sin($sa) / 2);
		$coords .= ", ${xp}, ${yp}";

		//If on the front half, add the thickness offset
		if ($sa >= M_PI && $sa <= 2 * M_PI * 1.01) {
			$yp = floor($yp + $thick);
			$coords .= ", ${xp}, ${yp}";
		}

		//add coordinates every 0.2 radians
		$a = $sa + 0.2;
		while ($a < $ea) {
			$xp = floor($width * cos($a) / 2 + $xc);
			if ($a >= M_PI && $a <= 2 * M_PI * 1.01) {
				$yp = floor($yc - ($height * sin($a) / 2) + $thick);
			} else {
				$yp = floor($yc - $height * sin($a) / 2);
			}
			$coords .= ", ${xp}, ${yp}";
			$a += 0.2;
		}

		//Add the last point on the arc
		$xp = floor($width * cos($ea) / 2 + $xc);
		$yp = floor($yc - $height * sin($ea) / 2);

		if ($ea >= M_PI && $ea <= 2 * M_PI * 1.01) {
			$coords .= ", ${xp}, " . floor($yp + $thick);
		}
		$coords .= ", ${xp}, ${yp}";
		$alt = '';

		if (!empty($this->csimtargets[$i])) {
			$this->csimareas .= "<area shape=\"poly\" coords=\"${coords}\" href=\"" . $this->csimtargets[$i] . '"';

			if (!empty($this->csimwintargets[$i])) {
				$this->csimareas .= ' target="' . $this->csimwintargets[$i] . '" ';
			}

			if (!empty($this->csimalts[$i])) {
				$tmp = sprintf($this->csimalts[$i], $this->data[$i]);
				$this->csimareas .= "alt=\"${tmp}\" title=\"${tmp}\" ";
			}
			$this->csimareas .= " />\n";
		}
	}

	public function SetLabels($aLabels, $aLblPosAdj = 'auto')
	{
		$this->labels	   = $aLabels;
		$this->ilabelposadj = $aLblPosAdj;
	}

	// Distance from the pie to the labels
	public function SetLabelMargin($m)
	{
		$this->value->SetMargin($m);
	}

	// Show a thin line from the pie to the label for a specific slice
	public function ShowLabelHint($f = true)
	{
		$this->showlabelhint = $f;
	}

	// Set color of hint line to label for each slice
	public function SetLabelHintColor($c)
	{
		$this->labelhintcolor = $c;
	}

	public function SetHeight($aHeight)
	{
		$this->iThickness = $aHeight;
	}

	// Normalize Angle between 0-360
	public function NormAngle($a)
	{
		// Normalize anle to 0 to 2M_PI
		//
		if ($a > 0) {
			while ($a > 360) {
				$a -= 360;
			}
		} else {
			while ($a < 0) {
				$a += 360;
			}
		}
		if ($a < 0) {
			$a = 360 + $a;
		}

		if ($a == 360) {
			$a = 0;
		}

		return $a;
	}

	// Draw one 3D pie slice at position ($xc,$yc) with height $z
	public function Pie3DSlice($img, $xc, $yc, $w, $h, $sa, $ea, $z, $fillcolor, $shadow = 0.65)
	{
		// Due to the way the 3D Pie algorithm works we are
		// guaranteed that any slice we get into this method
		// belongs to either the left or right side of the
		// pie ellipse. Hence, no slice will cross 90 or 270
		// point.
		if (($sa < 90 && $ea > 90) || (($sa > 90 && $sa < 270) && $ea > 270)) {
			Util\JpGraphError::RaiseL(14003); //('Internal assertion failed. Pie3D::Pie3DSlice');
			exit(1);
		}

		$p[] = [];

		// Setup pre-calculated values
		$rsa   = $sa / 180 * M_PI; // to Rad
		$rea   = $ea / 180 * M_PI; // to Rad
		$sinsa = sin($rsa);
		$cossa = cos($rsa);
		$sinea = sin($rea);
		$cosea = cos($rea);

		// p[] is the points for the overall slice and
		// pt[] is the points for the top pie

		// Angular step when approximating the arc with a polygon train.
		$step = 0.05;

		if ($sa >= 270) {
			if ($ea > 360 || ($ea > 0 && $ea <= 90)) {
				if ($ea > 0 && $ea <= 90) {
					// Adjust angle to simplify conditions in loops
					$rea += 2 * M_PI;
				}

				$p = [$xc, $yc, $xc, $yc + $z,
					$xc + $w * $cossa, $z + $yc - $h * $sinsa, ];
				$pt = [$xc, $yc, $xc + $w * $cossa, $yc - $h * $sinsa];

				for ($a = $rsa; $a < 2 * M_PI; $a += $step) {
					$tca  = cos($a);
					$tsa  = sin($a);
					$p[]  = $xc + $w * $tca;
					$p[]  = $z + $yc - $h * $tsa;
					$pt[] = $xc + $w * $tca;
					$pt[] = $yc - $h * $tsa;
				}

				$pt[] = $xc + $w;
				$pt[] = $yc;

				$p[] = $xc + $w;
				$p[] = $z + $yc;
				$p[] = $xc + $w;
				$p[] = $yc;
				$p[] = $xc;
				$p[] = $yc;

				for ($a = 2 * M_PI + $step; $a < $rea; $a += $step) {
					$pt[] = $xc + $w * cos($a);
					$pt[] = $yc - $h * sin($a);
				}

				$pt[] = $xc + $w * $cosea;
				$pt[] = $yc - $h * $sinea;
				$pt[] = $xc;
				$pt[] = $yc;
			} else {
				$p = [$xc, $yc, $xc, $yc + $z,
					$xc + $w * $cossa, $z + $yc - $h * $sinsa, ];
				$pt = [$xc, $yc, $xc + $w * $cossa, $yc - $h * $sinsa];

				$rea = $rea == 0.0 ? 2 * M_PI : $rea;
				for ($a = $rsa; $a < $rea; $a += $step) {
					$tca  = cos($a);
					$tsa  = sin($a);
					$p[]  = $xc + $w * $tca;
					$p[]  = $z + $yc - $h * $tsa;
					$pt[] = $xc + $w * $tca;
					$pt[] = $yc - $h * $tsa;
				}

				$pt[] = $xc + $w * $cosea;
				$pt[] = $yc - $h * $sinea;
				$pt[] = $xc;
				$pt[] = $yc;

				$p[] = $xc + $w * $cosea;
				$p[] = $z + $yc - $h * $sinea;
				$p[] = $xc + $w * $cosea;
				$p[] = $yc - $h * $sinea;
				$p[] = $xc;
				$p[] = $yc;
			}
		} elseif ($sa >= 180) {
			$p  = [$xc, $yc, $xc, $yc + $z, $xc + $w * $cosea, $z + $yc - $h * $sinea];
			$pt = [$xc, $yc, $xc + $w * $cosea, $yc - $h * $sinea];

			for ($a = $rea; $a > $rsa; $a -= $step) {
				$tca  = cos($a);
				$tsa  = sin($a);
				$p[]  = $xc + $w * $tca;
				$p[]  = $z + $yc - $h * $tsa;
				$pt[] = $xc + $w * $tca;
				$pt[] = $yc - $h * $tsa;
			}

			$pt[] = $xc + $w * $cossa;
			$pt[] = $yc - $h * $sinsa;
			$pt[] = $xc;
			$pt[] = $yc;

			$p[] = $xc + $w * $cossa;
			$p[] = $z + $yc - $h * $sinsa;
			$p[] = $xc + $w * $cossa;
			$p[] = $yc - $h * $sinsa;
			$p[] = $xc;
			$p[] = $yc;
		} elseif ($sa >= 90) {
			if ($ea > 180) {
				$p  = [$xc, $yc, $xc, $yc + $z, $xc + $w * $cosea, $z + $yc - $h * $sinea];
				$pt = [$xc, $yc, $xc + $w * $cosea, $yc - $h * $sinea];

				for ($a = $rea; $a > M_PI; $a -= $step) {
					$tca  = cos($a);
					$tsa  = sin($a);
					$p[]  = $xc + $w * $tca;
					$p[]  = $z + $yc - $h * $tsa;
					$pt[] = $xc + $w * $tca;
					$pt[] = $yc - $h * $tsa;
				}

				$p[] = $xc - $w;
				$p[] = $z + $yc;
				$p[] = $xc - $w;
				$p[] = $yc;
				$p[] = $xc;
				$p[] = $yc;

				$pt[] = $xc - $w;
				$pt[] = $z + $yc;
				$pt[] = $xc - $w;
				$pt[] = $yc;

				for ($a = M_PI - $step; $a > $rsa; $a -= $step) {
					$pt[] = $xc + $w * cos($a);
					$pt[] = $yc - $h * sin($a);
				}

				$pt[] = $xc + $w * $cossa;
				$pt[] = $yc - $h * $sinsa;
				$pt[] = $xc;
				$pt[] = $yc;
			} else {
				// $sa >= 90 && $ea <= 180
				$p = [$xc, $yc, $xc, $yc + $z,
					$xc + $w * $cosea, $z + $yc - $h * $sinea,
					$xc + $w * $cosea, $yc - $h * $sinea,
					$xc, $yc, ];

				$pt = [$xc, $yc, $xc + $w * $cosea, $yc - $h * $sinea];

				for ($a = $rea; $a > $rsa; $a -= $step) {
					$pt[] = $xc + $w * cos($a);
					$pt[] = $yc - $h * sin($a);
				}

				$pt[] = $xc + $w * $cossa;
				$pt[] = $yc - $h * $sinsa;
				$pt[] = $xc;
				$pt[] = $yc;
			}
		} else {
			// sa > 0 && ea < 90

			$p = [$xc, $yc, $xc, $yc + $z,
				$xc + $w * $cossa, $z + $yc - $h * $sinsa,
				$xc + $w * $cossa, $yc - $h * $sinsa,
				$xc, $yc, ];

			$pt = [$xc, $yc, $xc + $w * $cossa, $yc - $h * $sinsa];

			for ($a = $rsa; $a < $rea; $a += $step) {
				$pt[] = $xc + $w * cos($a);
				$pt[] = $yc - $h * sin($a);
			}

			$pt[] = $xc + $w * $cosea;
			$pt[] = $yc - $h * $sinea;
			$pt[] = $xc;
			$pt[] = $yc;
		}

		$img->PushColor($fillcolor . ':' . $shadow);
		$img->FilledPolygon($p);
		$img->PopColor();

		$img->PushColor($fillcolor);
		$img->FilledPolygon($pt);
		$img->PopColor();
	}

	public function SetStartAngle($aStart)
	{
		if ($aStart < 0 || $aStart > 360) {
			Util\JpGraphError::RaiseL(14004); //('Slice start angle must be between 0 and 360 degrees.');
		}
		$this->startangle = $aStart;
	}

	// Draw a 3D Pie
	public function Pie3D(
		$aaoption,
		$img,
		$data,
		$colors,
		$xc,
		$yc,
		$d,
		$angle,
		$z,
		$shadow = 0.65,
		$startangle = 0,
		$edgecolor = '',
		$edgeweight = 1
	) {
		/**
		 * As usual the algorithm get more complicated than I originally
		 * // envisioned. I believe that this is as simple as it is possible
		 * // to do it with the features I want. It's a good exercise to start
		 * // thinking on how to do this to convince your self that all this
		 * // is really needed for the general case.
		 * //
		 * // The algorithm two draw 3D pies without "real 3D" is done in
		 * // two steps.
		 * // First imagine the pie cut in half through a thought line between
		 * // 12'a clock and 6'a clock. It now easy to imagine that we can plot
		 * // the individual slices for each half by starting with the topmost
		 * // pie slice and continue down to 6'a clock.
		 * //
		 * // In the algortithm this is done in three principal steps
		 * // Step 1. Do the knife cut to ensure by splitting slices that extends
		 * // over the cut line. This is done by splitting the original slices into
		 * // upto 3 subslices.
		 * // Step 2. Find the top slice for each half
		 * // Step 3. Draw the slices from top to bottom
		 * //
		 * // The thing that slightly complicates this scheme with all the
		 * // angle comparisons below is that we can have an arbitrary start
		 * // angle so we must take into account the different equivalence classes.
		 * // For the same reason we must walk through the angle array in a
		 * // modulo fashion.
		 * //
		 * // Limitations of algorithm:
		 * // * A small exploded slice which crosses the 270 degree point
		 * //   will get slightly nagged close to the center due to the fact that
		 * //   we print the slices in Z-order and that the slice left part
		 * //   get printed first and might get slightly nagged by a larger
		 * //   slice on the right side just before the right part of the small
		 * //   slice. Not a major problem though.
		 */
		// Determine the height of the ellippse which gives an
		// indication of the inclination angle
		$h   = ($angle / 90.0) * $d;
		$sum = 0;
		for ($i = 0; $i < safe_count($data); ++$i) {
			$sum += $data[$i];
		}

		// Special optimization
		if ($sum == 0) {
			return;
		}

		if ($this->labeltype == 2) {
			$this->adjusted_data = $this->AdjPercentage($data);
		}

		// Setup the start
		$accsum = 0;
		$a	  = $startangle;
		$a	  = $this->NormAngle($a);

		//
		// Step 1 . Split all slices that crosses 90 or 270
		//
		$idx		= 0;
		$adjexplode = [];
		$numcolors  = safe_count($colors);
		for ($i = 0; $i < safe_count($data); ++$i, ++$idx) {
			$da = $data[$i] / $sum * 360;

			if (empty($this->explode_radius[$i])) {
				$this->explode_radius[$i] = 0;
			}

			$expscale = 1;
			if ($aaoption == 1) {
				$expscale = 2;
			}

			$la	  = $a + $da / 2;
			$explode = [$xc + $this->explode_radius[$i] * cos($la * M_PI / 180) * $expscale,
				$yc - $this->explode_radius[$i] * sin($la * M_PI / 180) * ($h / $d) * $expscale, ];
			$adjexplode[$idx]   = $explode;
			$labeldata[$i]	  = [$la, $explode[0], $explode[1]];
			$originalangles[$i] = [$a, $a + $da];

			$ne = $this->NormAngle($a + $da);
			if ($da <= 180) {
				// If the slice size is <= 90 it can at maximum cut across
				// one boundary (either 90 or 270) where it needs to be split
				$split = -1; // no split
				if (($da <= 90 && ($a <= 90 && $ne > 90)) ||
					(($da <= 180 && $da > 90) && (($a < 90 || $a >= 270) && $ne > 90))) {
					$split = 90;
				} elseif (($da <= 90 && ($a <= 270 && $ne > 270)) ||
					(($da <= 180 && $da > 90) && ($a >= 90 && $a < 270 && ($a + $da) > 270))) {
					$split = 270;
				}
				if ($split > 0) {
					// split in two
					$angles[$idx]	 = [$a, $split];
					$adjcolors[$idx]  = $colors[$i % $numcolors];
					$adjexplode[$idx] = $explode;
					$angles[++$idx]   = [$split, $ne];
					$adjcolors[$idx]  = $colors[$i % $numcolors];
					$adjexplode[$idx] = $explode;
				} else {
					// no split
					$angles[$idx]	 = [$a, $ne];
					$adjcolors[$idx]  = $colors[$i % $numcolors];
					$adjexplode[$idx] = $explode;
				}
			} else {
				// da>180
				// Slice may, depending on position, cross one or two
				// bonudaries

				if ($a < 90) {
					$split = 90;
				} elseif ($a <= 270) {
					$split = 270;
				} else {
					$split = 90;
				}

				$angles[$idx]	 = [$a, $split];
				$adjcolors[$idx]  = $colors[$i % $numcolors];
				$adjexplode[$idx] = $explode;
				//if( $a+$da > 360-$split ) {
				// For slices larger than 270 degrees we might cross
				// another boundary as well. This means that we must
				// split the slice further. The comparison gets a little
				// bit complicated since we must take into accound that
				// a pie might have a startangle >0 and hence a slice might
				// wrap around the 0 angle.
				// Three cases:
				//  a) Slice starts before 90 and hence gets a split=90, but
				//	 we must also check if we need to split at 270
				//  b) Slice starts after 90 but before 270 and slices
				//	 crosses 90 (after a wrap around of 0)
				//  c) If start is > 270 (hence the firstr split is at 90)
				//	 and the slice is so large that it goes all the way
				//	 around 270.
				if (($a < 90 && ($a + $da > 270)) || ($a > 90 && $a <= 270 && ($a + $da > 360 + 90)) || ($a > 270 && $this->NormAngle($a + $da) > 270)) {
					$angles[++$idx]   = [$split, 360 - $split];
					$adjcolors[$idx]  = $colors[$i % $numcolors];
					$adjexplode[$idx] = $explode;
					$angles[++$idx]   = [360 - $split, $ne];
					$adjcolors[$idx]  = $colors[$i % $numcolors];
					$adjexplode[$idx] = $explode;
				} else {
					// Just a simple split to the previous decided
					// angle.
					$angles[++$idx]   = [$split, $ne];
					$adjcolors[$idx]  = $colors[$i % $numcolors];
					$adjexplode[$idx] = $explode;
				}
			}
			$a += $da;
			$a = $this->NormAngle($a);
		}

		// Total number of slices
		$n = safe_count($angles);

		for ($i = 0; $i < $n; ++$i) {
			list($dbgs, $dbge) = $angles[$i];
		}

		//
		// Step 2. Find start index (first pie that starts in upper left quadrant)
		//
		$minval = $angles[0][0];
		$min	= 0;
		for ($i = 0; $i < $n; ++$i) {
			if ($angles[$i][0] < $minval) {
				$minval = $angles[$i][0];
				$min	= $i;
			}
		}
		$j   = $min;
		$cnt = 0;
		while ($angles[$j][1] <= 90) {
			++$j;
			if ($j >= $n) {
				$j = 0;
			}
			if ($cnt > $n) {
				Util\JpGraphError::RaiseL(14005);
				//("Pie3D Internal error (#1). Trying to wrap twice when looking for start index");
			}
			++$cnt;
		}
		$start = $j;

		//
		// Step 3. Print slices in z-order
		//
		$cnt = 0;

		// First stroke all the slices between 90 and 270 (left half circle)
		// counterclockwise

		while ($angles[$j][0] < 270 && $aaoption !== 2) {
			list($x, $y) = $adjexplode[$j];

			$this->Pie3DSlice(
				$img,
				$x,
				$y,
				$d,
				$h,
				$angles[$j][0],
				$angles[$j][1],
				$z,
				$adjcolors[$j],
				$shadow
			);

			$last = [$x, $y, $j];

			++$j;
			if ($j >= $n) {
				$j = 0;
			}

			if ($cnt > $n) {
				Util\JpGraphError::RaiseL(14006);
				//("Pie3D Internal Error: Z-Sorting algorithm for 3D Pies is not working properly (2). Trying to wrap twice while stroking.");
			}
			++$cnt;
		}

		$slice_left = $n - $cnt;
		$j		  = $start - 1;
		if ($j < 0) {
			$j = $n - 1;
		}

		$cnt = 0;

		// The stroke all slices from 90 to -90 (right half circle)
		// clockwise
		while ($cnt < $slice_left && $aaoption !== 2) {
			list($x, $y) = $adjexplode[$j];

			$this->Pie3DSlice(
				$img,
				$x,
				$y,
				$d,
				$h,
				$angles[$j][0],
				$angles[$j][1],
				$z,
				$adjcolors[$j],
				$shadow
			);
			--$j;
			if ($cnt > $n) {
				Util\JpGraphError::RaiseL(14006);
				//("Pie3D Internal Error: Z-Sorting algorithm for 3D Pies is not working properly (2). Trying to wrap twice while stroking.");
			}
			if ($j < 0) {
				$j = $n - 1;
			}

			++$cnt;
		}

		// Now do a special thing. Stroke the last slice on the left
		// halfcircle one more time.  This is needed in the case where
		// the slice close to 270 have been exploded. In that case the
		// part of the slice close to the center of the pie might be
		// slightly nagged.
		if ($aaoption !== 2) {
			$this->Pie3DSlice(
				$img,
				$last[0],
				$last[1],
				$d,
				$h,
				$angles[$last[2]][0],
				$angles[$last[2]][1],
				$z,
				$adjcolors[$last[2]],
				$shadow
			);
		}

		if ($aaoption !== 1) {
			// Now print possible labels and add csim
			$this->value->ApplyFont($img);
			$margin = $img->GetFontHeight() / 2 + $this->value->margin;
			for ($i = 0; $i < safe_count($data); ++$i) {
				$la = $labeldata[$i][0];
				$x  = $labeldata[$i][1] + cos($la * M_PI / 180) * ($d + $margin) * $this->ilabelposadj;
				$y  = $labeldata[$i][2] - sin($la * M_PI / 180) * ($h + $margin) * $this->ilabelposadj;
				if ($this->ilabelposadj >= 1.0) {
					if ($la > 180 && $la < 360) {
						$y += $z;
					}
				}
				if ($this->labeltype == 0) {
					if ($sum > 0) {
						$l = 100 * $data[$i] / $sum;
					} else {
						$l = 0;
					}
				} elseif ($this->labeltype == 1) {
					$l = $data[$i];
				} else {
					$l = $this->adjusted_data[$i];
				}
				if (isset($this->labels[$i]) && is_string($this->labels[$i])) {
					$l = sprintf($this->labels[$i], $l);
				}

				$this->StrokeLabels($l, $img, $labeldata[$i][0] * M_PI / 180, $x, $y, $z);

				$this->Add3DSliceToCSIM(
					$i,
					$labeldata[$i][1],
					$labeldata[$i][2],
					$h * 2,
					$d * 2,
					$z,
					$originalangles[$i][0],
					$originalangles[$i][1]
				);
			}
		}

		//
		// Finally add potential lines in pie
		//

		if ($edgecolor == '' || $aaoption !== 0) {
			return;
		}

		$accsum = 0;
		$a	  = $startangle;
		$a	  = $this->NormAngle($a);

		$a *= M_PI / 180.0;

		$idx = 0;
		$img->PushColor($edgecolor);
		$img->SetLineWeight($edgeweight);

		$fulledge = true;
		for ($i = 0; $i < safe_count($data) && $fulledge; ++$i) {
			if (empty($this->explode_radius[$i])) {
				$this->explode_radius[$i] = 0;
			}
			if ($this->explode_radius[$i] > 0) {
				$fulledge = false;
			}
		}

		for ($i = 0; $i < safe_count($data); ++$i, ++$idx) {
			$da = $data[$i] / $sum * 2 * M_PI;
			$this->StrokeFullSliceFrame(
				$img,
				$xc,
				$yc,
				$a,
				$a + $da,
				$d,
				$h,
				$z,
				$edgecolor,
				$this->explode_radius[$i],
				$fulledge
			);
			$a += $da;
		}
		$img->PopColor();
	}

	public function StrokeFullSliceFrame($img, $xc, $yc, $sa, $ea, $w, $h, $z, $edgecolor, $exploderadius, $fulledge)
	{
		$step = 0.02;

		if ($exploderadius > 0) {
			$la = ($sa + $ea) / 2;
			$xc += $exploderadius * cos($la);
			$yc -= $exploderadius * sin($la) * ($h / $w);
		}

		$p = [$xc, $yc, $xc + $w * cos($sa), $yc - $h * sin($sa)];

		for ($a = $sa; $a < $ea; $a += $step) {
			$p[] = $xc + $w * cos($a);
			$p[] = $yc - $h * sin($a);
		}

		$p[] = $xc + $w * cos($ea);
		$p[] = $yc - $h * sin($ea);
		$p[] = $xc;
		$p[] = $yc;

		$img->SetColor($edgecolor);
		$img->Polygon($p);

		// Unfortunately we can't really draw the full edge around the whole of
		// of the slice if any of the slices are exploded. The reason is that
		// this algorithm is to simply. There are cases where the edges will
		// "overwrite" other slices when they have been exploded.
		// Doing the full, proper 3D hidden lines stiff is actually quite
		// tricky. So for exploded pies we only draw the top edge. Not perfect
		// but the "real" solution is much more complicated.
		if ($fulledge && !($sa > 0 && $sa < M_PI && $ea < M_PI)) {
			if ($sa < M_PI && $ea > M_PI) {
				$sa = M_PI;
			}

			if ($sa < 2 * M_PI && (($ea >= 2 * M_PI) || ($ea > 0 && $ea < $sa))) {
				$ea = 2 * M_PI;
			}

			if ($sa >= M_PI && $ea <= 2 * M_PI) {
				$p = [$xc + $w * cos($sa), $yc - $h * sin($sa),
					$xc + $w * cos($sa), $z + $yc - $h * sin($sa), ];

				for ($a = $sa + $step; $a < $ea; $a += $step) {
					$p[] = $xc + $w * cos($a);
					$p[] = $z + $yc - $h * sin($a);
				}
				$p[] = $xc + $w * cos($ea);
				$p[] = $z + $yc - $h * sin($ea);
				$p[] = $xc + $w * cos($ea);
				$p[] = $yc - $h * sin($ea);
				$img->SetColor($edgecolor);
				$img->Polygon($p);
			}
		}
	}

	public function Stroke($img, $aaoption = 0)
	{
		$n = safe_count($this->data);

		// If user hasn't set the colors use the theme array
		if ($this->setslicecolors == null) {
			$colors = array_keys($img->rgb->rgb_table);
			sort($colors);
			$idx_a = $this->themearr[$this->theme];
			$ca	= [];
			$m	 = safe_count($idx_a);
			for ($i = 0; $i < $m; ++$i) {
				$ca[$i] = $colors[$idx_a[$i]];
			}
			$ca = array_reverse(array_slice($ca, 0, $n));
		} else {
			$ca = $this->setslicecolors;
		}

		if ($this->posx <= 1 && $this->posx > 0) {
			$xc = round($this->posx * $img->width);
		} else {
			$xc = $this->posx;
		}

		if ($this->posy <= 1 && $this->posy > 0) {
			$yc = round($this->posy * $img->height);
		} else {
			$yc = $this->posy;
		}

		if ($this->radius <= 1) {
			$width = floor($this->radius * min($img->width, $img->height));
			// Make sure that the pie doesn't overflow the image border
			// The 0.9 factor is simply an extra margin to leave some space
			// between the pie an the border of the image.
			$width = min($width, min($xc * 0.9, ($yc * 90 / $this->angle - $width / 4) * 0.9));
		} else {
			$width = $this->radius * ($aaoption === 1 ? 2 : 1);
		}

		// Add a sanity check for width
		if ($width < 1) {
			Util\JpGraphError::RaiseL(14007); //("Width for 3D Pie is 0. Specify a size > 0");
		}

		// Establish a thickness. By default the thickness is a fifth of the
		// pie slice width (=pie radius) but since the perspective depends
		// on the inclination angle we use some heuristics to make the edge
		// slightly thicker the less the angle.

		// Has user specified an absolute thickness? In that case use
		// that instead

		if ($this->iThickness) {
			$thick = $this->iThickness;
			$thick *= ($aaoption === 1 ? 2 : 1);
		} else {
			$thick = $width / 12;
		}
		$a = $this->angle;

		if ($a <= 30) {
			$thick *= 1.6;
		} elseif ($a <= 40) {
			$thick *= 1.4;
		} elseif ($a <= 50) {
			$thick *= 1.2;
		} elseif ($a <= 60) {
			$thick *= 1.0;
		} elseif ($a <= 70) {
			$thick *= 0.8;
		} elseif ($a <= 80) {
			$thick *= 0.7;
		} else {
			$thick *= 0.6;
		}

		$thick = floor($thick);

		if ($this->explode_all) {
			for ($i = 0; $i < $n; ++$i) {
				$this->explode_radius[$i] = $this->explode_r;
			}
		}

		$this->Pie3D(
			$aaoption,
			$img,
			$this->data,
			$ca,
			$xc,
			$yc,
			$width,
			$this->angle,
			$thick,
			0.65,
			$this->startangle,
			$this->edgecolor,
			$this->edgeweight
		);

		// Adjust title position
		if ($aaoption != 1) {
			$this->title->SetPos($xc, $yc - $this->title->GetFontHeight($img) - $width / 2 - $this->title->margin, 'center', 'bottom');
			$this->title->Stroke($img);
		}
	}

	/**
	 * PRIVATE METHODS.
	 *
	 * @param mixed $label
	 * @param mixed $img
	 * @param mixed $a
	 * @param mixed $xp
	 * @param mixed $yp
	 * @param mixed $z
	 */

	// Position the labels of each slice
	public function StrokeLabels($label, $img, $a, $xp, $yp, $z)
	{
		$this->value->halign = 'left';
		$this->value->valign = 'top';

		// Position the axis title.
		// dx, dy is the offset from the top left corner of the bounding box that sorrounds the text
		// that intersects with the extension of the corresponding axis. The code looks a little
		// bit messy but this is really the only way of having a reasonable position of the
		// axis titles.
		$this->value->ApplyFont($img);
		$h = $img->GetTextHeight($label);
		// For numeric values the format of the display value
		// must be taken into account
		if (is_numeric($label)) {
			if ($label >= 0) {
				$w = $img->GetTextWidth(sprintf($this->value->format, $label));
			} else {
				$w = $img->GetTextWidth(sprintf($this->value->negformat, $label));
			}
		} else {
			$w = $img->GetTextWidth($label);
		}

		while ($a > 2 * M_PI) {
			$a -= 2 * M_PI;
		}

		if ($a >= 7 * M_PI / 4 || $a <= M_PI / 4) {
			$dx = 0;
		}

		if ($a >= M_PI / 4 && $a <= 3 * M_PI / 4) {
			$dx = ($a - M_PI / 4) * 2 / M_PI;
		}

		if ($a >= 3 * M_PI / 4 && $a <= 5 * M_PI / 4) {
			$dx = 1;
		}

		if ($a >= 5 * M_PI / 4 && $a <= 7 * M_PI / 4) {
			$dx = (1 - ($a - M_PI * 5 / 4) * 2 / M_PI);
		}

		if ($a >= 7 * M_PI / 4) {
			$dy = (($a - M_PI) - 3 * M_PI / 4) * 2 / M_PI;
		}

		if ($a <= M_PI / 4) {
			$dy = (1 - $a * 2 / M_PI);
		}

		if ($a >= M_PI / 4 && $a <= 3 * M_PI / 4) {
			$dy = 1;
		}

		if ($a >= 3 * M_PI / 4 && $a <= 5 * M_PI / 4) {
			$dy = (1 - ($a - 3 * M_PI / 4) * 2 / M_PI);
		}

		if ($a >= 5 * M_PI / 4 && $a <= 7 * M_PI / 4) {
			$dy = 0;
		}

		$x = round($xp - $dx * $w);
		$y = round($yp - $dy * $h);

		// Mark anchor point for debugging
		/*
		$img->SetColor('red');
		$img->Line($xp-10,$yp,$xp+10,$yp);
		$img->Line($xp,$yp-10,$xp,$yp+10);
		 */

		$oldmargin		   = $this->value->margin;
		$this->value->margin = 0;
		$this->value->Stroke($img, $label, $x, $y);
		$this->value->margin = $oldmargin;
	}
} // @class

/* EOF */
